package com.xique.park.service.service.impl;

import cn.easyes.core.biz.PageInfo;
import cn.easyes.core.conditions.LambdaEsQueryWrapper;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xique.common.core.domain.R;
import com.xique.common.core.utils.AliossUtil;
import com.xique.common.core.utils.DateUtils;
import com.xique.common.core.utils.SecurityUtils;
import com.xique.common.core.utils.StringUtils;
import com.xique.common.core.utils.bean.BeanUtils;
import com.xique.common.core.web.domain.PageResult;
import com.xique.park.api.dto.ParkingOrderDTO;
import com.xique.park.bean.domain.*;
import com.xique.park.bean.dto.ParkInDto;
import com.xique.park.bean.dto.ParkOutDto;
import com.xique.park.bean.dto.PayOrderDto;
import com.xique.park.bean.request.web.parkingOrder.ParkingOrderRequest;
import com.xique.park.bean.transform.web.parkInAndOut.ParkInAndOutTransform;
import com.xique.park.bean.vo.web.parkIn.ParkInVo;
import com.xique.park.bean.vo.web.parkInAndOut.detail.ParkCouponDetailVo;
import com.xique.park.bean.vo.web.parkInAndOut.detail.ParkOrderDetailVo;
import com.xique.park.bean.vo.web.parkOut.ParkOutVo;
import com.xique.park.bean.vo.web.parkingOrder.ParkingOrderVo;
import com.xique.park.bean.vo.web.payOrder.PayOrderVo;
import com.xique.park.enums.PayChannelEnum;
import com.xique.park.mapper.ParkingOrderMapper;
import com.xique.park.service.service.*;
import com.xique.park.utils.DatesUtil;
import com.xique.system.api.RemoteSystemProjectService;
import com.xique.system.api.RemoteSystemUserService;
import com.xique.system.api.dto.OperateDTO;
import com.xique.system.api.dto.SystemProjectDTO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.compress.utils.Lists;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.search.aggregations.metrics.ParsedSum;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.stream.Collectors;

/**
 * 停车流水订单
 * -单次停车唯一标识Service业务层处理
 *
 * @author xique
 * @date 2021-12-09
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ParkingOrderServiceImpl extends ServiceImpl<ParkingOrderMapper, ParkingOrder> implements IParkingOrderService {

    private final IParkInService parkInService;

    private final IParkOutService parkOutService;

    private final IPayOrderService payOrderService;

    private final ParkingOrderMapper parkingOrderMapper;

    private final IParkingOrderEsService parkingOrderEsService;

    private final RemoteSystemUserService remoteSystemUserService;

    private final IParkCouponPpService parkCouponPpService;

    private final RemoteSystemProjectService remoteSystemProjectService;

    private final AliossUtil aliossUtil;

    private final ThreadPoolTaskExecutor threadPoolTaskExecutor;

    private final SqlSessionFactory sqlSessionFactory;

    /**
     * 查询停车流水订单
     * -单次停车唯一标识
     *
     * @param id 停车流水订单
     *           -单次停车唯一标识ID
     * @return 停车流水订单
     * -单次停车唯一标识
     */
    @Override
    public ParkingOrder selectParkingOrderById(Long id) {
        return parkingOrderMapper.selectParkingOrderById(id);
    }

    /**
     * 查询停车流水订单
     * -单次停车唯一标识列表
     *
     * @param parkingOrder 停车流水订单
     *                     -单次停车唯一标识
     * @return 停车流水订单
     * -单次停车唯一标识
     */
    @Override
    public List<ParkingOrder> selectParkingOrderList(ParkingOrder parkingOrder) {
        return parkingOrderMapper.selectParkingOrderList(parkingOrder);
    }

    /**
     * 新增停车流水订单
     * -单次停车唯一标识
     *
     * @param parkingOrder 停车流水订单
     *                     -单次停车唯一标识
     * @return 结果
     */
    @Override
    public int insertParkingOrder(ParkingOrder parkingOrder) {
        parkingOrder.setCreateTime(DateUtils.getNowDate());
        return parkingOrderMapper.insertParkingOrder(parkingOrder);
    }

    /**
     * 修改停车流水订单
     * -单次停车唯一标识
     *
     * @param parkingOrder 停车流水订单
     *                     -单次停车唯一标识
     * @return 结果
     */
    @Override
    public int updateParkingOrder(ParkingOrder parkingOrder) {
        parkingOrder.setUpdateTime(DateUtils.getNowDate());
        return parkingOrderMapper.updateParkingOrder(parkingOrder);
    }

    @Override
    public void updateByParkingSerial(ParkingOrder parkingOrder) {
        LambdaQueryWrapper<ParkingOrder> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(ParkingOrder::getParkingSerial, parkingOrder.getParkingSerial());
        update(parkingOrder, queryWrapper);
    }

    /**
     * 批量删除停车流水订单
     * -单次停车唯一标识
     *
     * @param ids 需要删除的停车流水订单
     *            -单次停车唯一标识ID
     * @return 结果
     */
    @Override
    public int deleteParkingOrderByIds(Long[] ids) {
        return parkingOrderMapper.deleteParkingOrderByIds(ids);
    }

    @Override
    public PageResult selectPageList(ParkingOrderRequest parkingOrderRequest) {
        Long parkId = parkingOrderRequest.getParkId();
        R<SystemProjectDTO> systemProjectR = remoteSystemProjectService.getProjectInfo(parkId);
        if (null != systemProjectR && null != systemProjectR.getData()) {
            SystemProjectDTO systemProject = systemProjectR.getData();
            String payChannel = systemProject.getPayChannel();
            if (StrUtil.equals(payChannel, PayChannelEnum.PP.getChannel())) {
                // PP
                return selectPageListToDb(parkingOrderRequest);
            } else if (StrUtil.equals(payChannel, PayChannelEnum.ICBC.getChannel())) {
                // ICBC
                return selectPageListToEs(parkingOrderRequest);
            } else {
                log.warn("[订单记录]-分页查询订单记录，支付渠道未匹配,{},{}", parkId, systemProject);
                return getEmptyPageResult(parkingOrderRequest);
            }
        } else {
            log.warn("[订单记录]-分页查询订单记录，车场信息获取失败,{}", parkId);
            return getEmptyPageResult(parkingOrderRequest);
        }
    }

    private PageResult getEmptyPageResult(ParkingOrderRequest request) {
        return PageResult.builder()
                .pageNum(request.getPageNum())
                .pageSize(request.getPageSize())
                .totalPages(0L)
                .totalSize(0L)
                .list(Lists.newArrayList())
                .num(0L)
                .totalPrice(BigDecimal.ZERO)
                .build();
    }

    /**
     * 根据条件查询订单列表 - ES
     *
     * @param request 查询参数
     * @return 查询结果
     */
    private PageResult selectPageListToEs(ParkingOrderRequest request) {
        try {
            // 统计总金额
            Future<BigDecimal> totalPriceFuture = threadPoolTaskExecutor.submit(() -> selectTotalPriceToEs(request));

            // 查询列表
            PageInfo<ParkingOrder> parkingOrderPageInfo = doSelectPageListToEs(request);

            PageResult pageResult = handlePageResult(parkingOrderPageInfo, request);
            pageResult.setTotalPrice(totalPriceFuture.get());
            return pageResult;
        } catch (ExecutionException e) {
            log.error("[订单记录]-ES分页查询订单列表，查询异常：{}，{}", request, e.getMessage());
            e.printStackTrace();
            return getEmptyPageResult(request);
        } catch (InterruptedException e) {
            log.error("[订单记录]-ES分页查询订单列表，查询异常：{}，{}", request, e.getMessage());
            e.printStackTrace();
            Thread.currentThread().interrupt();
            return getEmptyPageResult(request);
        }
    }

    private PageInfo<ParkingOrder> doSelectPageListToEs(ParkingOrderRequest request) {
        long startTime = System.currentTimeMillis();
        LambdaEsQueryWrapper<ParkingOrder> queryWrapper = getQueryWrapper(request);
        queryWrapper.orderByDesc(ParkingOrder::getInTime);
        PageInfo<ParkingOrder> parkingOrderPageInfo = parkingOrderEsService.pageQuery(queryWrapper, request.getPageNum(), request.getPageSize());
        log.info("[订单记录]-ES分页查询订单列表，查询耗时：{}", System.currentTimeMillis() - startTime);
        return parkingOrderPageInfo;
    }

    /**
     * 根据条件查询订单总金额 - ES
     *
     * @param request 查询条件
     * @return 订单总金额
     */
    private BigDecimal selectTotalPriceToEs(ParkingOrderRequest request) {
        long startTime = System.currentTimeMillis();
        LambdaEsQueryWrapper<ParkingOrder> queryWrapper = getQueryWrapper(request);

        queryWrapper.sum(ParkingOrder::getTotalFee);
        SearchResponse response = parkingOrderEsService.search(queryWrapper);
        ParsedSum totalFee = (ParsedSum) response.getAggregations().getAsMap().get("totalFeeSum");
        log.info("[订单记录]-ES统计订单总金额，统计耗时：{}", System.currentTimeMillis() - startTime);

        if (null == totalFee) {
            return BigDecimal.ZERO;
        }
        return BigDecimal.valueOf(totalFee.getValue());
    }

    private LambdaEsQueryWrapper<ParkingOrder> getQueryWrapper(ParkingOrderRequest request) {
        LambdaEsQueryWrapper<ParkingOrder> queryWrapper = new LambdaEsQueryWrapper<>();
        queryWrapper.eq(null != request.getParkId(), ParkingOrder::getParkId, request.getParkId());
        queryWrapper.matchPhrase(StrUtil.isNotBlank(request.getPlate()), ParkingOrder::getPlate, request.getPlate());
        queryWrapper.eq(StrUtil.isNotBlank(request.getMngTypeId()), ParkingOrder::getMngType, request.getMngTypeId());
        queryWrapper.eq(StrUtil.isNotBlank(request.getParkStatus()), ParkingOrder::getParkStatus, request.getParkStatus());
        queryWrapper.in(CollUtil.isNotEmpty(request.getPayStatus()), ParkingOrder::getPayStatus, request.getPayStatus());
        // 大于等于
        if (StrUtil.isNotBlank(request.getStartTime())) {
            Date startTime = DateUtil.parseDateTime(request.getStartTime());
            long startTimeMilliseconds = startTime.getTime();
            queryWrapper.ge(ParkingOrder::getInTime, startTimeMilliseconds);
        }
        // 小于等于
        if (StrUtil.isNotBlank(request.getEndTime())) {
            Date endTime = DateUtil.parseDateTime(request.getEndTime());
            long endTimeMilliseconds = endTime.getTime();
            queryWrapper.le(ParkingOrder::getInTime, endTimeMilliseconds);
        }
        return queryWrapper;
    }

    /**
     * 处理分页查询请求结果 - ES
     *
     * @param parkingOrderPageInfo ES 分页查询结果
     * @param request              查询参数
     * @return /
     */
    private PageResult handlePageResult(PageInfo<ParkingOrder> parkingOrderPageInfo, ParkingOrderRequest request) {
        List<ParkingOrder> parkingOrderList = parkingOrderPageInfo.getList();

        // 根据停车流水号去查询支付信息
        if (CollUtil.isNotEmpty(parkingOrderList)) {
            parkingOrderList.forEach(parkingOrder -> {
                long parkId = parkingOrder.getParkId();
                String parkingSerial = parkingOrder.getParkingSerial();
                List<PayOrderDto> payOrderDtoList = payOrderService.selectPayOrderByParkingSerial(parkId, parkingSerial);
                if (CollUtil.isNotEmpty(payOrderDtoList)) {
                    parkingOrder.setPaySsn(payOrderDtoList.get(0).getPaySsn());
                }
            });
        }

        return PageResult.builder()
                .pageNum(request.getPageNum())
                .pageSize(request.getPageSize())
                .totalPages((long) parkingOrderPageInfo.getPages())
                .totalSize(parkingOrderPageInfo.getTotal())
                .list(parkingOrderList)
                .num(parkingOrderPageInfo.getTotal())
                .build();
    }

    /**
     * 根据条件查询订单列表 - MySQL
     *
     * @param parkingOrderRequest 查询参数
     * @return 查询结果
     */
    private PageResult selectPageListToDb(ParkingOrderRequest parkingOrderRequest) {
        Page<ParkingOrderVo> page = new Page<>();
        page.setCurrent(parkingOrderRequest.getPageNum());
        page.setSize(parkingOrderRequest.getPageSize());
        IPage<ParkingOrderVo> iPage = parkingOrderMapper.selectPageList(page, parkingOrderRequest);
        return handlePageResult(iPage, parkingOrderRequest);
    }


    @Override
    public ParkingOrderVo selectStatisticalInfo(ParkingOrderRequest parkingOrderRequest) {
        return parkingOrderMapper.selectStatisticalInfo(parkingOrderRequest);
    }

    /**
     * 处理分页查询结果 - MySQL
     */
    private PageResult handlePageResult(IPage<ParkingOrderVo> iPage, ParkingOrderRequest parkingOrderRequest) {
        try {
            Future<ParkingOrderVo> parkingOrderVoFuture = threadPoolTaskExecutor.submit(() -> selectStatisticalInfo(parkingOrderRequest));

            List<ParkingOrderVo> parkingOrderVoPageList = iPage.getRecords();
            long parkId = SecurityUtils.getParkId();
            Future<List<ParkingOrderVo>> parkingOrderVoListFuture = threadPoolTaskExecutor.submit(() -> {
                List<ParkingOrderVo> parkingOrderVoList = new ArrayList<>();
                // 根据ID去查询详细信息
                if (CollUtil.isNotEmpty(parkingOrderVoPageList)) {
                    List<Long> parkingOrderIds = new ArrayList<>();
                    parkingOrderVoPageList.forEach(parkingOrderVo -> parkingOrderIds.add(parkingOrderVo.getId()));
                    parkingOrderVoList = batchSelectByIds(parkingOrderIds, parkId);
                }

                // 根据停车流水号去查询支付信息
                if (CollUtil.isNotEmpty(parkingOrderVoList)) {
                    parkingOrderVoList.forEach(parkingOrderVo -> {
                        String parkingSerial = parkingOrderVo.getParkingSerial();
                        List<PayOrderDto> payOrderDtoList = payOrderService.selectPayOrderByParkingSerial(parkId, parkingSerial);
                        if (CollUtil.isNotEmpty(payOrderDtoList)) {
                            parkingOrderVo.setPaySsn(payOrderDtoList.get(0).getPaySsn());
                        }
                    });
                }
                return parkingOrderVoList;
            });

            List<ParkingOrderVo> parkingOrderVoList = parkingOrderVoListFuture.get();

            // 去重
            parkingOrderVoList = duplicateRemoval(parkingOrderVoList);

            ParkingOrderVo parkingOrderVo = parkingOrderVoFuture.get();
            long num = parkingOrderVo.getTotalCount();
            BigDecimal totalPrice = parkingOrderVo.getTotalPrice();

            long totalPages = iPage.getPages();
            long totalSize = iPage.getTotal();
            return PageResult.builder().pageNum(parkingOrderRequest.getPageNum())
                    .pageSize(parkingOrderRequest.getPageSize()).totalPages(totalPages)
                    .totalSize(totalSize).list(parkingOrderVoList).num(num)
                    .totalPrice(null != totalPrice ? totalPrice : BigDecimal.ZERO)
                    .build();
        } catch (Exception e) {
            e.printStackTrace();
            if (e instanceof InterruptedException) {
                Thread.currentThread().interrupt();
            }
            return PageResult.builder()
                    .pageNum(parkingOrderRequest.getPageNum()).pageSize(parkingOrderRequest.getPageSize())
                    .totalPages(0L).totalSize(0L)
                    .list(null).num(0L)
                    .totalPrice(BigDecimal.ZERO)
                    .build();
        }
    }

    private List<ParkingOrderVo> duplicateRemoval(List<ParkingOrderVo> parkingOrderVoList) {
        List<ParkingOrderVo> resultList = new ArrayList<>();
        if (CollUtil.isNotEmpty(parkingOrderVoList)) {
            Set<String> parkingSerialSet = new HashSet<>();
            for (ParkingOrderVo parkingOrderVo : parkingOrderVoList) {
                String parkingSerial = parkingOrderVo.getParkingSerial();
                if (StrUtil.isNotBlank(parkingSerial) && !parkingSerialSet.contains(parkingSerial)) {
                    parkingSerialSet.add(parkingSerial);
                    resultList.add(parkingOrderVo);
                }
            }

            ListUtil.reverse(resultList);
        }
        return resultList;
    }

    private List<ParkingOrderVo> batchSelectByIds(List<Long> ids, Long parkId) {
        return parkingOrderMapper.batchSelectByIds(ids, parkId);
    }

    @Override
    public ParkingOrderVo selectDetailInfo(Long parkId, String parkingSerial, Long parkingOrderId) {
        R<SystemProjectDTO> systemProjectR = remoteSystemProjectService.getProjectInfo(parkId);
        if (null != systemProjectR && null != systemProjectR.getData()) {
            SystemProjectDTO systemProject = systemProjectR.getData();
            String payChannel = systemProject.getPayChannel();
            if (StrUtil.equals(payChannel, PayChannelEnum.PP.getChannel())) {
                // PP
                return selectDetailInfoFromDb(parkId, parkingSerial);
            } else if (StrUtil.equals(payChannel, PayChannelEnum.ICBC.getChannel())) {
                // ICBC
                return selectDetailInfoFromEs(parkId, parkingSerial, parkingOrderId);
            } else {
                log.warn("[订单记录]-查询订单详情，支付渠道未匹配,{},{}", parkId, systemProject);
                return new ParkingOrderVo();
            }
        } else {
            log.warn("[订单记录]-分页查询支付记录，车场信息获取失败,{}", parkId);
            return new ParkingOrderVo();
        }

    }

    private ParkingOrderVo selectDetailInfoFromEs(Long parkId, String parkingSerial, Long parkingOrderId) {
        ParkingOrder parkingOrder = parkingOrderEsService.selectById(parkingOrderId);
        ParkingOrderVo parkingOrderVo = new ParkingOrderVo();
        if (null != parkingOrder) {

            ParkOrderDetailVo parkOrderDetailVo = new ParkOrderDetailVo();
            parkOrderDetailVo.setTotalFee(parkingOrder.getTotalFee());
            parkOrderDetailVo.setActualFee(parkingOrder.getPayFee());
            parkOrderDetailVo.setMayFee(parkingOrder.getTotalFee());
            parkOrderDetailVo.setReduceFee(parkingOrder.getCouponFee());
            if (null != parkingOrder.getParkingTime()) {
                parkOrderDetailVo.setStopTime(DatesUtil.toDhmsStyle(parkingOrder.getParkingTime()));
            }
            parkingOrderVo.setParkOrderDetailVo(parkOrderDetailVo);

            ParkInVo parkIn = new ParkInVo();
            parkIn.setInCarPlate(parkingOrder.getInCarPlate());
            parkIn.setInPic(StringUtils.isNotEmpty(parkingOrder.getInPic()) ? aliossUtil.getJzyGetObjectUrl(parkingOrder.getInPic()).toString() : "");
            parkIn.setInRemark(parkingOrder.getInRemark());
            parkIn.setInTime(parkingOrder.getInTime());
            parkIn.setInType(parkingOrder.getInType());
            parkIn.setLaneName(parkingOrder.getInLaneName());
            parkIn.setMngName(parkingOrder.getCarMngtypeName());
            parkIn.setMngType(parkingOrder.getMngType());
            parkIn.setStatus(parkingOrder.getInStatus());
            parkingOrderVo.setParkIn(parkIn);

            ParkOutVo parkOut = new ParkOutVo();
            parkOut.setOutCarPlate(parkingOrder.getOutCarPlate());
            parkOut.setOutPic(StringUtils.isNotEmpty(parkingOrder.getOutPic()) ? aliossUtil.getJzyGetObjectUrl(parkingOrder.getOutPic()).toString() : "");
            parkOut.setOutRemark(parkingOrder.getOutRemark());
            parkOut.setOutTime(parkingOrder.getOutTime());
            parkOut.setOutType(parkingOrder.getOutType());
            parkOut.setLaneName(parkingOrder.getOutLaneName());
            parkOut.setMngName(parkingOrder.getCarMngtypeName());
            parkOut.setMngType(parkingOrder.getMngType());
            parkOut.setStatus(parkingOrder.getOutStatus());
            parkingOrderVo.setParkOut(parkOut);

            // 查询支付记录
            List<PayOrderVo> payOrderVoList = selectPayOrderByParkingSerial(parkId, parkingSerial);
            parkingOrderVo.setPayOrderList(payOrderVoList);

            // 查询停车券记录
            ParkingOrderDTO parkingOrderDTO = new ParkingOrderDTO();
            parkingOrderDTO.setPlate(parkingOrder.getPlate());
            List<ParkCouponDetailVo> parkCouponDetailVoList = selectParkCouponPpList(parkOrderDetailVo, parkingOrderDTO, parkId, parkingSerial);
            parkingOrderVo.setParkCouponPpList(parkCouponDetailVoList);
        }
        return parkingOrderVo;
    }

    public ParkingOrderVo selectDetailInfoFromDb(Long parkId, String parkingSerial) {
        ParkingOrderVo parkingOrderVo = new ParkingOrderVo();
        ParkOrderDetailVo parkOrderDetailVo = new ParkOrderDetailVo();
        try {
            // 查询订单信息
            Future<ParkingOrderDTO> parkingOrderDTOFuture = threadPoolTaskExecutor.submit(() -> selectParkingOrderByParkingSerial(parkOrderDetailVo, parkId, parkingSerial));

            // 查询入场详细信息
            Future<ParkInVo> parkInVoFuture = threadPoolTaskExecutor.submit(() -> selectParkInByParkingSerial(parkId, parkingSerial));

            // 查询出场详细信息
            Future<ParkingOrderVo> parkingOrderVoFuture = threadPoolTaskExecutor.submit(() -> selectParkOutByParkingSerial(parkingOrderVo, parkOrderDetailVo, parkId, parkingSerial));

            // 查询支付记录
            Future<List<PayOrderVo>> payOrderVoListFuture = threadPoolTaskExecutor.submit(() -> selectPayOrderByParkingSerial(parkId, parkingSerial));

            ParkingOrderDTO parkingOrderDTO = parkingOrderDTOFuture.get();
            // 查询停车券记录
            List<ParkCouponDetailVo> parkCouponDetailVoList = selectParkCouponPpList(parkOrderDetailVo, parkingOrderDTO, parkId, parkingSerial);
            parkingOrderVo.setParkCouponPpList(parkCouponDetailVoList);

            parkingOrderVo.setParkIn(parkInVoFuture.get());
            parkingOrderVoFuture.get();
            parkingOrderVo.setPayOrderList(payOrderVoListFuture.get());

            return checkParkInAndOut(parkingOrderVo, parkingOrderDTO);
        } catch (Exception e) {
            e.printStackTrace();
            if (e instanceof InterruptedException) {
                Thread.currentThread().interrupt();
            }
            return parkingOrderVo;
        }
    }

    /**
     * 处理进出记录信息，三个月前的进出记录被删除了，从订单记录中获取
     */
    private ParkingOrderVo checkParkInAndOut(ParkingOrderVo parkingOrderVo, ParkingOrderDTO parkingOrderDTO) {
        if (null != parkingOrderVo && null != parkingOrderDTO) {
            ParkInVo parkIn = parkingOrderVo.getParkIn();

            parkIn.setInCarPlate(StrUtil.isNotBlank(parkIn.getInCarPlate()) ? parkIn.getInCarPlate() : parkingOrderDTO.getInCarPlate());
            parkIn.setLaneName(StrUtil.isNotBlank(parkIn.getLaneName()) ? parkIn.getLaneName() : parkingOrderDTO.getInLaneName());
            parkIn.setInType(StrUtil.isNotBlank(parkIn.getInType()) ? parkIn.getInType() : parkingOrderDTO.getInType());
            parkIn.setStatus(StrUtil.isNotBlank(parkIn.getStatus()) ? parkIn.getStatus() : parkingOrderDTO.getInStatus());
            parkIn.setInRemark(StrUtil.isNotBlank(parkIn.getInRemark()) ? parkIn.getInRemark() : parkingOrderDTO.getInRemark());
            parkIn.setMngName(StrUtil.isNotBlank(parkIn.getMngName()) ? parkIn.getMngName() : parkingOrderDTO.getCarMngtypeName());
            parkIn.setInTime(null != parkIn.getInTime() ? parkIn.getInTime() : parkingOrderDTO.getInTime());

            if (StrUtil.isBlank(parkIn.getInPic()) && StrUtil.isNotBlank(parkingOrderDTO.getInPic())) {
                String inPic = aliossUtil.getJzyGetObjectUrl(parkingOrderDTO.getInPic()).toString();
                parkIn.setInPic(inPic);
            }
            parkingOrderVo.setParkIn(parkIn);

            ParkOutVo parkOut = parkingOrderVo.getParkOut();

            parkOut.setOutCarPlate(StrUtil.isNotBlank(parkOut.getOutCarPlate()) ? parkOut.getOutCarPlate() : parkingOrderDTO.getOutCarPlate());
            parkOut.setLaneName(StrUtil.isNotBlank(parkOut.getLaneName()) ? parkOut.getLaneName() : parkingOrderDTO.getOutLaneName());
            parkOut.setOutType(StrUtil.isNotBlank(parkOut.getOutType()) ? parkOut.getOutType() : parkingOrderDTO.getOutType());
            parkOut.setStatus(StrUtil.isNotBlank(parkOut.getStatus()) ? parkOut.getStatus() : parkingOrderDTO.getOutStatus());
            parkOut.setOutRemark(StrUtil.isNotBlank(parkOut.getOutRemark()) ? parkOut.getOutRemark() : parkingOrderDTO.getOutRemark());
            parkOut.setMngName(StrUtil.isNotBlank(parkOut.getMngName()) ? parkOut.getMngName() : parkingOrderDTO.getCarMngtypeName());
            parkOut.setOutTime(null != parkOut.getOutTime() ? parkOut.getOutTime() : parkingOrderDTO.getOutTime());

            if (StrUtil.isBlank(parkOut.getOutPic()) && StrUtil.isNotBlank(parkingOrderDTO.getOutPic())) {
                String outPic = aliossUtil.getJzyGetObjectUrl(parkingOrderDTO.getOutPic()).toString();
                parkOut.setOutPic(outPic);
            }

            parkingOrderVo.setParkOut(parkOut);
        }
        return parkingOrderVo;
    }

    @Override
    public void updateBatchByParkIdAndParkingSerial(List<ParkingOrder> parkingOrderList) {
        if (CollUtil.isNotEmpty(parkingOrderList)) {
            long startTime = System.currentTimeMillis();
            SqlSession sqlSession = null;
            try {
                sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
                ParkingOrderMapper parkingOrderMapper = sqlSession.getMapper(ParkingOrderMapper.class);
                parkingOrderList.forEach(parkingOrderMapper::updateByParkIdAndParkingSerial);
                sqlSession.commit();
                sqlSession.clearCache();
            } catch (Exception e) {
                log.info("[订单记录服务]-批量更新异常，异常信息：{}", e.getMessage());
                e.printStackTrace();
                if (sqlSession != null) {
                    sqlSession.rollback();
                }
            } finally {
                if (sqlSession != null) {
                    sqlSession.close();
                }
            }
            log.info("[订单记录服务]-批量更新耗时：{}毫秒", System.currentTimeMillis() - startTime);
        }
    }

    /**
     * 查询停车券记录
     */
    private List<ParkCouponDetailVo> selectParkCouponPpList(ParkOrderDetailVo parkOrderDetailVo, ParkingOrderDTO parkingOrderDTO, Long parkId, String parkingSerial) {
        ParkCouponPp parkCouponPp = new ParkCouponPp();
        parkCouponPp.setParkId(parkId);
        parkCouponPp.setParkingSerial(parkingSerial);
        parkCouponPp.setPlate(parkingOrderDTO == null ? "" : parkingOrderDTO.getPlate());
        List<ParkCouponPp> couponList = parkCouponPpService.selectParkCouponPpList(parkCouponPp);
        List<ParkCouponDetailVo> parkCouponDetailVoList = new ArrayList<>(couponList.size());
        if (CollectionUtils.isNotEmpty(couponList)) {
            couponList.forEach(item -> {
                ParkCouponDetailVo vo = ParkInAndOutTransform.couponTransformCouponDetailVo(item);
                vo.setTotalFee(parkOrderDetailVo.getTotalFee());
                vo.setMayFee(parkOrderDetailVo.getMayFee());
                vo.setReduceFee(parkOrderDetailVo.getReduceFee());
                vo.setActualFee(parkOrderDetailVo.getActualFee());
                parkCouponDetailVoList.add(vo);
            });
        }
        return parkCouponDetailVoList;
    }

    /**
     * 查询支付记录
     */
    private List<PayOrderVo> selectPayOrderByParkingSerial(Long parkId, String parkingSerial) {
        List<PayOrderDto> payOrderDtoList = payOrderService.selectPayOrderByParkingSerial(parkId, parkingSerial);
        List<PayOrderVo> payOrderVoList = new ArrayList<>();
        if (payOrderDtoList != null && !payOrderDtoList.isEmpty()) {
            payOrderVoList = payOrderDtoList.stream().map(payOrderDto -> PayOrderVo.builder()
                            .payTime(payOrderDto.getPayTime()).payType(payOrderDto.getPayType()).status(payOrderDto.getStatus())
                            .transType(payOrderDto.getTransType()).shopCharge(payOrderDto.getShopCharge()).amt(payOrderDto.getAmt())
                            .actualFee(payOrderDto.getActualFee()).paidFee(payOrderDto.getPaidFee()).reduceFee(payOrderDto.getReduceFee())
                            .totalFee(payOrderDto.getTotalFee())
                            .build())
                    .collect(Collectors.toList());
        }
        return payOrderVoList;
    }

    /**
     * 查询出场详细信息
     */
    private ParkingOrderVo selectParkOutByParkingSerial(ParkingOrderVo parkingOrderVo, ParkOrderDetailVo parkOrderDetailVo, Long parkId, String parkingSerial) {
        List<ParkOutDto> parkOutDtoList = parkOutService.selectParkOutByParkingSerial(parkId, parkingSerial);
        ParkOutVo parkOutVo = new ParkOutVo();
        if (CollectionUtils.isNotEmpty(parkOutDtoList)) {
            ParkOutDto parkOutDto = parkOutDtoList.get(0);
            parkOrderDetailVo.setReduceFee(parkOutDto.getReduceFee().longValue());
            parkOrderDetailVo.setActualFee(parkOutDto.getActualFee().longValue());
            parkOrderDetailVo.setMayFee(parkOutDto.getMayFee().longValue());
            if (parkOutDto.getStopTime() != null) {
                parkOrderDetailVo.setStopTime(DatesUtil.toDhmsStyle(parkOutDto.getStopTime()));
            }
            BeanUtils.copyBeanProp(parkOutVo, parkOutDto);
            parkOutVo.setOutPic(StringUtils.isNotEmpty(parkOutVo.getOutPic()) ? aliossUtil.getJzyGetObjectUrl(parkOutVo.getOutPic()).toString() : "");
            R<OperateDTO> result = remoteSystemUserService.getOperate(parkOutDto.getUpdateUser());
            if (result.getData() != null) {
                parkOutVo.setUpdateBy(result.getData().getOperateName());
            }
        }
        parkingOrderVo.setParkOrderDetailVo(parkOrderDetailVo);
        parkingOrderVo.setParkOut(parkOutVo);

        return parkingOrderVo;
    }

    /**
     * 查询入场详细信息
     */
    private ParkInVo selectParkInByParkingSerial(Long parkId, String parkingSerial) {
        List<ParkInDto> parkInDtoList = parkInService.selectParkInByParkingSerial(parkId, parkingSerial);
        ParkInVo parkInVo = new ParkInVo();
        if (CollectionUtils.isNotEmpty(parkInDtoList)) {
            ParkInDto parkInDto = parkInDtoList.get(0);
            BeanUtils.copyBeanProp(parkInVo, parkInDto);
            parkInVo.setInPic(StringUtils.isNotEmpty(parkInVo.getInPic()) ? aliossUtil.getJzyGetObjectUrl(parkInVo.getInPic()).toString() : "");
            R<OperateDTO> result = remoteSystemUserService.getOperate(parkInDto.getUpdateUser());
            if (result.getData() != null) {
                parkInVo.setUpdateBy(result.getData().getOperateName());
            }
        }
        return parkInVo;
    }

    /**
     * 查询订单信息
     */
    private ParkingOrderDTO selectParkingOrderByParkingSerial(ParkOrderDetailVo parkOrderDetailVo, Long parkId, String parkingSerial) {
        // 查询订单信息
        ParkingOrderDTO parkingOrderDTO = selectParkingOrderByParkingSerial(parkId, parkingSerial);
        if (parkingOrderDTO != null) {
            parkOrderDetailVo.setPkSerial(parkingOrderDTO.getParkingSerial());
            if (null != parkingOrderDTO.getTotalFee()) {
                parkOrderDetailVo.setTotalFee(parkingOrderDTO.getTotalFee().longValue());
            }
            parkOrderDetailVo.setUpdateTime(parkingOrderDTO.getUpdateTime());
        }
        return parkingOrderDTO;
    }

    private ParkingOrderDTO selectParkingOrderByParkingSerial(Long parkId, String parkingSerial) {
        List<ParkingOrderDTO> parkingOrderDTOList = parkingOrderMapper.selectParkingOrderByParkingSerial(parkId, parkingSerial);
        if (CollectionUtils.isNotEmpty(parkingOrderDTOList)) {
            return parkingOrderDTOList.get(0);
        }
        return null;
    }

    @Override
    public void migrateDate(Long parkIdRequest) {
        long startTime = System.currentTimeMillis();

        LambdaQueryWrapper<ParkingOrder> queryWrapper = Wrappers.lambdaQuery();
        if (null != parkIdRequest) {
            queryWrapper.eq(ParkingOrder::getParkId, parkIdRequest);
        }

        long totalSize = count(queryWrapper) + 10000L;
        long pageSize = 10000;
        long totalPageSize = totalSize / pageSize;

        for (int pageNum = 1; pageNum <= totalPageSize; pageNum++) {
            long thisTime = System.currentTimeMillis();
            Page<ParkingOrder> page = new Page<>();
            page.setCurrent(pageNum);
            page.setSize(pageSize);

            page.setSearchCount(false);
            Page<ParkingOrder> parkingOrderPage = parkingOrderMapper.selectAllPage(page, parkIdRequest);

            List<ParkingOrder> parkingOrderList = parkingOrderPage.getRecords();

            if (CollUtil.isNotEmpty(parkingOrderList)) {
                // 新增ES中的订单信息
                parkingOrderEsService.batchSave(parkingOrderList);
            }

            log.info("[订单记录服务]数据迁移，总共{}页，当前迁移到第{}页，还剩{}页，本页耗时：{}ms，总耗时：{}s", totalPageSize, pageNum,
                    totalPageSize - pageNum,
                    System.currentTimeMillis() - thisTime,
                    (System.currentTimeMillis() - startTime) / 1000);
        }
    }

}
